// scaup - Scala up!
// (c) 2008-2009, Normen Müller, normen.mueller@gmail.com  
// $Id: Parser.scala 38 2009-11-10 14:32:44Z normen.mueller $
package scaup.parsing.dot

import scaup.Views._
import scaup.util.Misc._
import scala.util.parsing.combinator._
import scala.util.parsing.combinator.syntactical._

/** 
 * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 * @version 0.0.2 */
private[dot] class Parser extends StdTokenParsers with ImplicitConversions {
  /* Fill in abstract defs*/
  type Tokens = Lexer
  val lexical: Tokens = new Lexer
  
  /* Configure lexical parsing */                                 
  lexical.reserved ++= List("strict", "graph", "digraph", "node", "edge", "subgraph")
  lexical.delimiters ++= List("{", "}", "[", "]", ":", "=", ";", ",", "->", "--", "\"")
  
  /* Define the grammar */
  lazy val graph = opt("strict") ~ ("graph" ^^^ false | "digraph" ^^^ true) ~ opt(id) ~ ("{" ~> stmts <~ "}") ^^ {
    case s~t~id~ss => Graph(!s.isEmpty,t,id,ss:_*)
  }
  
  private lazy val stmts: Parser[List[Statement]] = rep(stmt <~ opt(";")) ^^ {
    case ss => List.flatten(ss)
  }
  
  private lazy val stmt = 
    ( gattr    ^^ {case s => List(s)}
    | edge
    | subgraph ^^ {case s => List(s)}
    | node     ^^ {case s => List(s)}
    )
  
  private lazy val gattr =
    ( attrp                                 ^^ {case a => Attrs("graph", a)}
    | (("graph" | "node" | "edge") ~ attrs) ^^ {case t~as => Attrs(t, as:_*)}
    )
  
  private lazy val edge = ((nid | subgraph) <~ ("->" | "--")) ~ rep1sep((nid | subgraph), ("->" | "--")) ~ attrs ^^ {
    case hd~tl~as => hd::tl zip (tl) map (e => Edge(as, e._1, e._2))
  }
  
  private lazy val subgraph = opt("subgraph") ~> opt(id) ~ ("{" ~> stmts <~ "}") ^^ {case id~ss => Subgraph(id,ss:_*)}  
  
  private lazy val node = id ~ attrs ^^ {case id~as => Node(id,as:_*)}
  
  private lazy val nid = id ^^ {case id => Node(id)}  
  
  private lazy val attrs = (("[" ~> ((attrp <~ opt(","))*) <~ "]")*) ^^ {case ls => List.flatten(ls)}
  
  private lazy val attrp = (id <~ "=") ~ id ^^ {case k~v   => Attr(k, v)}
  
  private lazy val id  = string | ident | numeric
  
  private lazy val string = accept("string",  {case lexical.StringLit(n) => n mkString ("\"", "", "\"") })
  
  private lazy val numeric = accept("numeric", {case lexical.NumericLit(n) => n })
  //def ident    = accept("identifier", {case lexical.Identifier(n) => n.toString })
}
